package cate.game.rank.h;

import cate.common.util.GameResult;
import cate.game.GameBody;
import cate.game.db.facade.RankDaoFacade;
import cate.game.event.EventPublisher;
import cate.game.event.rank.RewardRankChangeEvent;
import cate.game.log.GameLoggerFactory;
import cate.game.rank.po.Rank;
import cate.game.rank.po.RankSPO;
import cate.game.rank.vo.RankParam;
import cate.game.role.Role;
import cate.game.util.DebugGameConf;

import java.util.List;

/** [抽象]排行榜处理器 */
public abstract class RankHandler<E extends RankSPO<T>, T extends Rank> implements IRankFuncHandler<T> , EventPublisher {
    protected E rankSPO;

    protected GameBody gameBody;

    public RankHandler(){

    }

    /** 获取排行榜的标记 */
    protected abstract String getTag();

    protected abstract E createSPO();

    protected RankDaoFacade getDao(GameBody gameBody){
        return gameBody.db.rank;
    }

    /** 获取MongoTool */
    protected synchronized E getSPO(GameBody game) {
        if (rankSPO == null) {
            String key = "rank_" + getTag();
            rankSPO = getDao(game).get(key);
            if(rankSPO == null){
                rankSPO = createSPO();
            }
            rankSPO.key = key;
        }
        return rankSPO;
    }

    /** 初始化时需要加载排行榜数据 */
    public synchronized void initialize(GameBody gameBody) {
        this.gameBody = gameBody;
        if(DebugGameConf.debugFastStartup) {
            return;
        }
        //注册自己
        this.gameBody.rank.registeHandler(this);
        rankSPO = getSPO(gameBody);
        if (rankSPO != null) {
            rankSPO.initialize();
        }
    }

    /** 保存数据库的最小间隔时间 */
    private static final long SAVE_INTERVAL = 60000;
    /** 最近一次保存数据库的时间 */
    private long lastSaveTime = 0;
    /** 保存排行榜到数据库 */
    public synchronized void save() {
        long now = System.currentTimeMillis();
        if (now - lastSaveTime > SAVE_INTERVAL) {
            forceSave();
        }
    }

    /** 强制保存 */
    public synchronized void forceSave() {
        try{
            getDao(gameBody).save(rankSPO);
            lastSaveTime = System.currentTimeMillis();
        }catch(Exception e){
            GameLoggerFactory.getLogger(gameBody, this.getClass()).error("保存排行榜{}失败", this.getClass());
        }
    }

    /** 清空排行榜缓存、db (部分排行榜数据需要) */
    public synchronized void clearList() {
        getSPO(gameBody).clear();
        getDao(gameBody).save(rankSPO);
    }

    /**
     * 记录,默认只支持一个value的排行
     */
    public void record(Role role, long newValue) {
        RankParam param = RankParam.build(role);
        long preValue = getRankValue(param, 0);
        synchronized(this){
            getSPO(gameBody).record(role, newValue);
            save();
        }
        afterRecord(preValue, param);
    }

    public void afterRecord(long preValue, RankParam param) {
        if(getRankId() > 0){
            Rank rank0 = getRankOneData(param);
            if(rank0 != null && rank0.value > preValue){
                publishEvent(gameBody, new RewardRankChangeEvent(gameBody, getRankId(), rank0));
            }
        }
    }

    public void updateRole(Role role) {
        getSPO(gameBody).updateRole(role);
    }

    public synchronized int getIndex(String uid) {
        return getSPO(gameBody).getIndex(uid);
    }

    @Override
    public synchronized T getData(RankParam param, Role role){
        return getByUid(role.getUid());
    }

    public synchronized T getByUid(String uid){
        return getSPO(gameBody).findByRoleUid(uid);
    }

    @Override
    public List<T> getRankList(RankParam param){
        return getSPO(gameBody).getList();
    }

    public long getRankValue(RankParam param, int index){
        T data = getDataByIndex(param, index);
        return data == null ? 0 : data.getValue();
    }

    public T getDataByIndex(RankParam param, int index){
        List<T> rankList = getRankList(param);
        if(index >= rankList.size()){
            return null;
        }
        return rankList.get(index);
    }

    @Override
    public GameResult<Void> admire(RankParam param, String roleUid, int rank){
        GameResult<Void> r = new GameResult<>();
        //不做并发处理，多几次少几次无所谓
        List<T> list = getRankList(param);
        if (rank < 0 || rank >= list.size()) {
            return r.fail("超出排行榜范围");
        }
        T t = list.get(rank);
        if(t == null){
            return r.fail("查不到此人数据");
        }
        if(t.getOwner().uid.equals(roleUid)){
            return r.fail("无法给自己点赞");
        }
        t.admireNum += 1;
        return r;
    }

    @Override
    public T getRankOneData(RankParam param){
        return getDataByIndex(param,0);
    }
}
